package JasPerl::Util::Number;

use 5.010;
use strict;
use warnings;

use JasPerl::Util::Exceptions;

use Scalar::Util qw(blessed looks_like_number);

my %COERCIONS = (
    'Math::BigFloat' => sub {
        return $_[0]->JasPerl::Util::Number::new($_[1]);
    },
    'Math::BigInt' => sub {
        return $_[0]->JasPerl::Util::Number::new($_[1]);
    },
    'Math::BigRat' => sub {
        return $_[0]->JasPerl::Util::Number::new($_[1]);
    }
);

sub new {
    my ($class, $value) = @_;
    bless \$value, $class;
}

sub parse {
    my ($class, $s) = @_;
    JasPerl::Util::NullPointerException->throw()
        unless defined $s;
    JasPerl::Util::NumberFormatException->throw("Not a number: '$s'")
        unless looks_like_number($s);
    bless \$s, $class;
}

sub from {
    my ($class, $from) = @_;
    # $class->parse handles undef
    return $class->parse($from) unless ref $from;

    if (blessed $from) {
        return $from if $from->isa($class);

        foreach my $type (keys %COERCIONS) {
            return $COERCIONS{$type}->($class, $from) if $from->isa($type);
        }
    }

    JasPerl::Util::CoercionException->throw(
        "Cannot create $class from " . (ref $from || 'scalar')
    );
}

sub value {
    return ${$_[0]};
}

sub as_string {
    return '' . $_[0]->value;
}

use overload (
    '0+' => 'value',
    '""' => 'as_string',
    'int' => sub { int($_[0]->value) }
);

1;
